package com.tn.service.vehicle.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gitee.hifong45.FE;
import com.tn.controller.alarm.common.constants.Constants;
import com.tn.controller.alarm.common.enums.CommonCodeEnum;
import com.tn.controller.alarm.common.enums.TallyStatusEnum;
import com.tn.controller.alarm.common.enums.TruckStatusEnum;
import com.tn.controller.alarm.common.model.PageInput;
import com.tn.controller.alarm.common.model.PageResult;
import com.tn.controller.alarm.common.model.map.WarningVO;
import com.tn.controller.alarm.common.model.rpm.mq.EventMQDTO;
import com.tn.controller.alarm.common.model.vehicle.entity.VehicleInfoDO;
import com.tn.controller.alarm.common.model.vehicle.vo.AlarmInputParam;
import com.tn.controller.alarm.common.model.vehicle.vo.VehicleInParam;
import com.tn.controller.alarm.common.model.vehicle.vo.VehicleTaskInfoVO;
import com.tn.controller.alarm.common.model.vehicle.vo.VehicleWorkWeightVO;
import com.tn.dao.mbpdao.*;
import com.tn.dao.vehicle.IVehicleDao;
import com.tn.exception.CommonException;
import com.tn.exception.DatabaseOperationFailedException;
import com.tn.service.alarm.IAlarmService;
import com.tn.service.map.IMapService;
import com.tn.service.vehicle.IVehicleService;
import com.tn.service.voice.SendVoiceDataMQ;
import com.tn.utils.DateUtil;
import com.tn.vo.SubTaskVo;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 装载车service实现类
 *
 * @program: tn_aucy_java
 * @author: yangjiayao
 * @create: 2022/03/01
 */
@Slf4j
@Service
public class VehicleServiceImpl implements IVehicleService {
    /**
     * 装载车告警
     */
    private static final int VEHICLE_WARNING_TYPE = 1;

    /**
     * 是否开启围栏监测
     */
    @Value("${alarm.enable:false}")
    private boolean enableAlarm;

    /**
     * 是否开启语音提示
     */
    @Value("${voice.enable:false}")
    private boolean enableVoice;

    /**
     * 达到匹配距离语音提示
     */
    @Value("${voice.tally.withinDistance}")
    private String withinDistance;

    /**
     * 超出匹配距离语音提示
     */
    @Value("${voice.tally.outOfDistance}")
    private String outOfDistance;

    /**
     * 装载车呼叫语音提示
     */
    @Value("${voice.tally.sendCall}")
    private String sendCall;

    /**
     * 装载车取消呼叫语音提示
     */
    @Value("${voice.tally.cancelCall}")
    private String cancelCall;

    /**
     * 开始理货语音提示
     */
    @Value("${voice.tally.matched}")
    private String matched;

    /**
     * 暂停理货语音提示
     */
    @Value("${voice.tally.pausedTask}")
    private String pausedTask;

    /**
     * 继续理货语音提示
     */
    @Value("${voice.tally.continueTask}")
    private String continueTask;

    /**
     * 完成理货语音提示
     */
    @Value("${voice.tally.completeTask}")
    private String completeTask;

    /**
     * 装载车数据库交互Dao
     */
    private IVehicleDao vehicleDao;

    /**
     * 发送语音提示
     */
    private SendVoiceDataMQ sendVoice;

    /**
     * 告警service
     */
    private IAlarmService alarmService;

    /**
     * 地图service
     */
    private IMapService mapService;

    /**
     * 自动注入
     *
     * @param vehicleDao vehicleDao
     */
    @Autowired
    public void setVehicleDao(IVehicleDao vehicleDao) {
        this.vehicleDao = vehicleDao;
    }

    /**
     * 自动注入
     *
     * @param sendVoice sendVoice
     */
    @Autowired
    public void setSendVoice(SendVoiceDataMQ sendVoice) {
        this.sendVoice = sendVoice;
    }

    /**
     * 自动注入
     *
     * @param mapService mapService
     */
    @Autowired
    public void setMapService(IMapService mapService) {
        this.mapService = mapService;
    }

    /**
     * 自动注入
     *
     * @param alarmService alarmService
     */
    @Autowired
    public void setAlarmService(IAlarmService alarmService) {
        this.alarmService = alarmService;
    }

    @Override
    public List<VehicleInfoDO> queryInactiveVehicle() {
        return vehicleDao.queryInactiveVehicle();
    }

    @Override
    public VehicleInfoDO isBoundDriver(String driver) {
        return vehicleDao.queryVehicleByDriver(driver);
    }

    @Override
    @Transactional
    public void boundDriver(int vehicleId, String driver) throws DatabaseOperationFailedException {
        if (null == isBoundDriver(driver)) {
            if (1 > vehicleDao.bindingDriver(vehicleId, driver)) {
                throw new DatabaseOperationFailedException(CommonCodeEnum.NOT_EXISTS.getCode(),
                        CommonCodeEnum.NOT_EXISTS.getMessage());
            }
        } else {
            throw new DatabaseOperationFailedException(CommonCodeEnum.EXISTS.getCode(),
                    CommonCodeEnum.EXISTS.getMessage());
        }
    }

    @Override
    @Transactional
    public void unboundDriver(String driver) throws DatabaseOperationFailedException {
        if (0 < vehicleDao.isWorkingDriver(driver)) {
            if (1 > vehicleDao.unboundDriver(driver)) {
                throw new DatabaseOperationFailedException(CommonCodeEnum.NOT_EXISTS.getCode(),
                        CommonCodeEnum.NOT_EXISTS.getMessage());
            }
        }
    }

    @Override
    public List<VehicleTaskInfoVO> queryVehicleTaskInfo(int vehicleId) {
        return vehicleDao.queryTallyTaskInfoByVehicle(vehicleId);
    }

    @Override
    @Transactional
    public boolean becomeReachable(String tagId) {
        // 播放语音提示
        sendVoice(tagId, withinDistance);
        return vehicleDao.updateTallyStatusByTag(tagId, TallyStatusEnum.WITHIN_DISTANCE.getCode(),
                DateUtil.dateToString(new Date())) > 0;
    }

    @Override
    @Transactional
    public boolean becomeUnreachable(String tagId) {
        // 播放语音提示
        sendVoice(tagId, outOfDistance);
        return vehicleDao.updateTallyStatusByTag(tagId, TallyStatusEnum.INIT.getCode(),
                DateUtil.dateToString(new Date())) > 0;
    }

    @Override
    @Transactional
    public VehicleTaskInfoVO sendCall(int taskId, int vehicleId, String license) throws DatabaseOperationFailedException {
        if (1 > vehicleDao.isWorking(vehicleId)) {
            throw new DatabaseOperationFailedException(CommonCodeEnum.PARAM_CONFLICT.getCode(),
                    CommonCodeEnum.PARAM_CONFLICT.getMessage());
        }
        //配合前端调试临时注释
//        if (1 > vehicleDao.updateTallyStatus(taskId, TallyStatusEnum.CALL_SENT.getCode(),
//                DateUtil.dateToString(new Date()))) {
//            throw new DatabaseOperationFailedException(CommonCodeEnum.NOT_EXISTS.getCode(),
//                    CommonCodeEnum.NOT_EXISTS.getMessage());
//        }
        // 播放语音提示
        sendTaskVoice(taskId, sendCall.replace("{}", license));

        return vehicleDao.queryTallyTaskInfoById(taskId);
    }

    @Override
    @Transactional
    public VehicleTaskInfoVO cancelCall(int taskId, String license) throws DatabaseOperationFailedException {
        if (1 > vehicleDao.isCalled(taskId)) {
            throw new DatabaseOperationFailedException(CommonCodeEnum.NOT_EXISTS.getCode(),
                    CommonCodeEnum.NOT_EXISTS.getMessage());
        }
        if (1 > vehicleDao.updateTallyStatus(taskId, TallyStatusEnum.WITHIN_DISTANCE.getCode(),
                DateUtil.dateToString(new Date()))) {
            throw new DatabaseOperationFailedException(CommonCodeEnum.NOT_EXISTS.getCode(),
                    CommonCodeEnum.NOT_EXISTS.getMessage());
        }
        // 播放语音提示
        sendTaskVoice(taskId, cancelCall.replace("{}", license));
        return vehicleDao.queryTallyTaskInfoById(taskId);
    }

    @Override
    @Transactional
    public VehicleTaskInfoVO matched(int taskId) throws DatabaseOperationFailedException {
        if (1 > vehicleDao.updateTallyStatus(taskId, TallyStatusEnum.WORKING.getCode(),
                DateUtil.dateToString(new Date()))) {
            throw new DatabaseOperationFailedException(CommonCodeEnum.NOT_EXISTS.getCode(),
                    CommonCodeEnum.NOT_EXISTS.getMessage());
        }
        // 创建告警监测任务
        if (enableAlarm) {
            int alarmId = alarmService.createAlarmTask(vehicleDao.queryAlarmParamById(taskId));
            if (0 >= alarmId || 0 >= vehicleDao.updateAlarmTaskId(taskId, alarmId)) {
                throw new CommonException(CommonCodeEnum.UNKNOWN.getCode(),
                        CommonCodeEnum.UNKNOWN.getMessage());
            }
        }
        // 播放语音提示
        sendTaskVoice(taskId, matched);
        return vehicleDao.queryTallyTaskInfoById(taskId);
    }

    @Override
    @Transactional
    public VehicleTaskInfoVO pausedTask(int taskId) throws DatabaseOperationFailedException {
        if (1 > vehicleDao.updateTallyStatus(taskId, TallyStatusEnum.PAUSE.getCode(),
                DateUtil.dateToString(new Date()))) {
            throw new DatabaseOperationFailedException(CommonCodeEnum.NOT_EXISTS.getCode(),
                    CommonCodeEnum.NOT_EXISTS.getMessage());
        }
        sendTaskVoice(taskId, pausedTask);
        return vehicleDao.queryTallyTaskInfoById(taskId);
    }

    @Override
    @Transactional
    public VehicleTaskInfoVO continueTask(int taskId) throws DatabaseOperationFailedException {
        if (1 > vehicleDao.updateTallyStatus(taskId, TallyStatusEnum.WORKING.getCode(),
                DateUtil.dateToString(new Date()))) {
            throw new DatabaseOperationFailedException(CommonCodeEnum.NOT_EXISTS.getCode(),
                    CommonCodeEnum.NOT_EXISTS.getMessage());
        }
        sendTaskVoice(taskId, continueTask);
        return vehicleDao.queryTallyTaskInfoById(taskId);
    }

    @Override
    @Transactional
    public void completeTask(int taskId) throws DatabaseOperationFailedException {
        // 删除告警检测
        if (enableAlarm) {
            alarmService.deleteAlarmTask(vehicleDao.queryAlarmParamById(taskId).getAlarmTaskId());
        }
        // 理货任务拷贝到结果表
        if (1 > vehicleDao.updateTallyStatus(taskId, TallyStatusEnum.COMPLETE.getCode(),
                DateUtil.dateToString(new Date()))) {
            throw new DatabaseOperationFailedException(CommonCodeEnum.NOT_EXISTS.getCode(),
                    CommonCodeEnum.NOT_EXISTS.getMessage());
        }
        if (1 > vehicleDao.copyTallyTaskToLog(taskId)) {
            throw new DatabaseOperationFailedException(CommonCodeEnum.UNKNOWN.getCode(),
                    CommonCodeEnum.UNKNOWN.getMessage());
        }
        if (1 > vehicleDao.deleteTallyTask(taskId)) {
            throw new DatabaseOperationFailedException(CommonCodeEnum.NOT_EXISTS.getCode(),
                    CommonCodeEnum.NOT_EXISTS.getMessage());
        }
        // TODO 同步完成状态给生产系统
        // 发送语音提示
        sendTaskVoice(taskId, completeTask);
    }

    @Override
    public PageResult<VehicleTaskInfoVO> queryHisTaskInfo(PageInput<VehicleInParam> pageParam) {
        int total = vehicleDao.queryHisTallyTaskCount(pageParam.getData().getVehicleId());
        PageResult<VehicleTaskInfoVO> result = new PageResult<>(pageParam.getCurPage(), pageParam.getPageSize(), total);
        if (0 < total) {
            result.setData(vehicleDao.queryHisTallyTaskByPage(result.getStartIndex(), pageParam.getPageSize(),
                    pageParam.getData().getVehicleId()));
        }
        return result;
    }

    @Override
    public VehicleWorkWeightVO queryWorkWeight(int vehicleId) {
        return new VehicleWorkWeightVO(vehicleDao.queryDailyDoneTaskByVehicle(vehicleId,
                DateUtil.dateToString(new Date(), "yyyy-MM-dd")),
                vehicleDao.queryDailyUndoTaskByVehicle(vehicleId,
                        DateUtil.dateToString(new Date(), "yyyy-MM-dd")));
    }

    @Override
    public void vehicleAlarmDataHandle(EventMQDTO eventMQDTORpmMqDTO) {
        List<String> objs = eventMQDTORpmMqDTO.getObjects();
        if (CollectionUtils.isNotEmpty(objs)) {
            String tagId = objs.get(0);
            AlarmInputParam alarmInputParam = vehicleDao.queryAlarmParamByTag(tagId);

            // 保存告警信息
            alarmService.saveAlarmInfo(alarmInputParam);

            // 发送告警标签信息给地图
            WarningVO warningVO = new WarningVO();
            warningVO.setTagId(tagId);
            warningVO.setLicense(alarmInputParam.getLicense());
            warningVO.setType(VEHICLE_WARNING_TYPE);
            mapService.sendWarningToMap(warningVO);
        }
    }

    /**
     * 发送语音提示
     *
     * @param taskId  任务ID
     * @param content 语音内容
     */
    private void sendTaskVoice(int taskId, String content) {
        if (enableVoice) {
            String tagId = vehicleDao.queryTruckTagByTallyId(taskId);
            sendVoice.sendVoiceTextData(tagId, content);
        }
    }

    /**
     * 发送语音提示
     *
     * @param tagId   标签ID
     * @param content 语音内容
     */
    private void sendVoice(String tagId, String content) {
        if (enableVoice) {
            sendVoice.sendVoiceTextData(tagId, content);
        }
    }

    @Resource
    private TaskInfoMapper taskInfoMapper;
    @Resource
    private WhitelistMapper whitelistMapper;
    @Resource
    private SubTaskVoMapper subTaskVoMapper;
    @Resource
    private VehicleTaskMapper vehicleTaskMapper;

    public Map<String, List<TaskInfo>> getAllTask(String license, int days) {
        String a = "驳-场";
        String b = "驳-火";
        String c = "场-火";
        List<String> list = new ArrayList<>();
        list.add(a);
        list.add(b);
        list.add(c);

        // 查询“未接-未完成”的任务
        List<TaskInfo> outLst = taskInfoMapper.selectList(new QueryWrapper<TaskInfo>()
                .gt("ctime", cn.hutool.core.date.DateUtil.offsetDay(cn.hutool.core.date.DateUtil.date(), -1 * days))
                .ne("deleted", Constants.DELETE_YES)
                .notIn("notice_system", list)
                .orderByDesc("id")
        );

        ArrayList<TaskInfo> outLst1 = outLst.stream().collect(
                Collectors.collectingAndThen(
                        Collectors.toCollection(
                                () -> new TreeSet<>(Comparator.comparing(TaskInfo::getTaskNo))
                        )
                        , ArrayList::new
                )
        );

        Map<String, List<TaskInfo>> mp = new HashMap<>();
        mp.put("accepted", new ArrayList<TaskInfo>());
        mp.put("not", outLst1);
        return mp;
    }



    public List<Object> getAcceptedTask(String license) {
        List<Object> lst = vehicleTaskMapper.selectObjs(new QueryWrapper<VehicleTask>()
                .eq("vehicle_license", license)
                .lt("status", Constants.STATUS_FINISHED)
                .orderByDesc("id")
        );
        return lst;
    }

    public void handleTask(String license, Integer taskId, String taskNo, Integer type) {
        // 查看是否已接任务
        List<VehicleTask> lst = vehicleTaskMapper.selectList(new QueryWrapper<VehicleTask>()
                .eq("vehicle_license", license)
                .eq("task_id", taskId)
                .orderByDesc("id")
        );
        if (lst.size() > 0) {
            // 如果已有，则更新
            VehicleTask vt = lst.get(0);
            vt.setStatus(type);
            vt.setStatusUpdateTime(cn.hutool.core.date.DateUtil.now());
            vehicleTaskMapper.updateById(vt);
            log.info("装载车已接任务更新完成: {}", vt);
        } else {
            // 原本没有，新增
            VehicleTask vt = new VehicleTask();
            vt.setTaskId(taskId);
            vt.setTaskNo(taskNo);
            vt.setVehicleLicense(license);
            vt.setStatus(type);
            vt.setStatusUpdateTime(cn.hutool.core.date.DateUtil.now());
            vehicleTaskMapper.insert(vt);
            log.info("装载车已接任务新增完成: {}", vt);
        }
    }

    public List<SubTaskVo> getSubTask(String taskNo) {
        List<SubTaskVo> lst = subTaskVoMapper.selectList(new QueryWrapper<SubTaskVo>()
                .eq("task_no", taskNo)
                .ne("state", Constants.STATUS_UNKNOWN)
                .ne("state", Constants.STATUS_FINISHED)
                .ne("state", TruckStatusEnum.ALREADY_TALLYING.getStatus())
                .ne("state", TruckStatusEnum.ALREADY_OUT.getStatus())
                .orderByDesc("id")
        );
        return lst;
    }

    // 处理子任务状态，其实就是白名单里的state
    public void handleSubTask(String subTaskId, Integer type, String vehicleNo) {
        SubTaskVo st = subTaskVoMapper.selectById(subTaskId);
        FE.nullThrow(st, "无此子任务: " + subTaskId);

        if (type == Constants.STATUS_FINISHED) {
            type = TruckStatusEnum.ALREADY_TALLYING.getStatus(); // 用“理货结束”替代“完成”
        }
        st.setOtherTaskInfo(vehicleNo); // 装载车牌
        st.setState(type);
        subTaskVoMapper.updateById(st);
        log.info("更新子任务完成： {}, {}", subTaskId, type);
    }

    public List<SubTaskVo> getMatchTruck(String vehicleNo) {
        // 一天内
        var lst = subTaskVoMapper.selectList(new QueryWrapper<SubTaskVo>()
                .select("other_task_info", "Count(license) As license")
                .gt("start_time", cn.hutool.core.date.DateUtil.beginOfDay(new Date()))
                .lt("end_time", cn.hutool.core.date.DateUtil.endOfDay(new Date()))
                .isNotNull("other_task_info")
                .gt("len(other_task_info)", 4)
                .groupBy("other_task_info")
        );
        return lst;
    }
}
